/*************************************************************************
 *                                                                       *
 * This file is part of the FreeMAPs distribution.  For full copyright   *
 * information, see COPYRIGHT and COPYING.LESSER.                        *
 *                                                                       *
 * Author:           sqbang                                              *
 * Email:            sqbang@ustc.edu                                     *
 * Date:             2011.03.30                                          *
 *                                                                       *
 *************************************************************************/

namespace FreeMAPs
{

template<typename T>
inline
std::set<T>
Utilities::type_cast(
    const std::set<int>& src)
{
    std::set<T> dest;
    for (std::set<int>::const_iterator pos = src.begin();
            pos != src.end(); ++pos)
    {
        dest.insert (static_cast<T>(*pos));
    }

    return dest;
}


template<typename T>
inline
std::vector<T>
Utilities::type_cast(
    const std::vector<int>& src)
{
    std::vector<T> dest;
    dest.resize (src.size());
    for (std::size_t pos = 0; pos != src.size(); ++pos)
    {
        dest[pos] = src[pos];
    }

    return dest;
}


template <typename ForwardIter, typename T>
inline void
Utilities::iota (
    ForwardIter first,
    ForwardIter last, T value)
{
    while (first != last)
    {
        *first = value++;
        ++first;
    }
}


inline int
Utilities::get_num_of_nodes_in_cell (
    const int dim)
{
    switch (dim)
    {
    case 2:
        {
            return 4;
        }
    case 3:
        {
            return 8;
        }
    default:
        {
            FREEMAPS_ERROR ("not supported dim");
            return -1;
        }
    }
}


template <typename Key, typename Value, typename Comp>
void
Utilities::clear_pointer_object (
    std::map<Key, Value*, Comp>& obj)
{
    while (!obj.empty())
    {
        typename std::map<Key, Value*, Comp>::iterator pos = obj.begin();

        Value* val = pos->second;
        if (val)
        {
            delete val;
        }

        val = NULL;

        obj.erase (pos);
    }

    obj.clear();
}


template <typename T>
void
Utilities::clear_pointer_object (
    std::vector<T*>& obj)
{
    while (!obj.empty())
    {
        typename std::vector<T*>::iterator pos = obj.begin();

        T* val = *pos;
        if (val)
        {
            delete val;
        }

        val = NULL;

        obj.erase (pos);
    }

    obj.clear();
}


template <typename T>
void
Utilities::clear_pointer_object (
    std::list<T*>& obj)
{
    while (!obj.empty())
    {
        typename std::list<T*>::iterator pos = obj.begin();

        T* val = *pos;
        if (val)
        {
            delete val;
        }

        val = NULL;

        obj.erase (pos);
    }

    obj.clear();
}


template <typename T>
void
Utilities::clear_pointer_object (
    T* obj)
{
    if (obj)
    {
        delete obj;
    }

    obj = NULL;
}


inline bool
Utilities::is_blank_string (
    const std::string& str)
{
    bool is_blank = true;
    if (str == FREEMAPS_EMPTY_STRING)
    {
        is_blank = true;
    }
    else
    {
        for (std::size_t i = 0; i < str.size(); ++i)
        {
            if (!std::isspace (str[i]))
            {
                is_blank = false;
                break;
            }
        }
    }

    return is_blank;
}


template <typename T, typename Cmp>
std::string
Utilities::get_rearranged_set (
    const std::set<T, Cmp>& array,
    const bool add_quot_mark,
    const int num_in_one_line)
{
    std::ostringstream os;
    int i = 0;
    int count = static_cast<int>(array.size());
    for (typename std::set<T, Cmp>::const_iterator pa = array.begin(); pa != array.end(); ++pa, ++i)
    {
        os << (add_quot_mark ? "\"" : "") << *pa << (add_quot_mark ? "\"" : "");
        if (i != count - 1)
        {
            os << ", ";
        }

        if (i != count - 1 && (i + 1) % num_in_one_line == 0)
        {
            os << "\n";
        }
    }
    return os.str();
}


template <typename T>
std::string
Utilities::get_rearranged_vector (
    const std::vector<T>& array,
    const bool add_quot_mark,
    const int num_in_one_line)
{
    const int size_array = static_cast<int>(array.size());
    std::ostringstream os;
    for (int i = 0; i < size_array; ++i)
    {
        os << (add_quot_mark ? "\"" : "") << array[i] << (add_quot_mark ? "\"" : "");
        if (i != size_array - 1)
        {
            os << ", ";
        }

        if (i != size_array - 1 && (i + 1) % num_in_one_line == 0)
        {
            os << "\n";
        }
    }
    return os.str();
}



template <typename T>
std::string
Utilities::get_rearranged_array (
    const T* array,
    const int size_array,
    const bool add_quot_mark,
    const int num_in_one_line)
{
    FREEMAPS_ASSERT (size_array >= 0);
    std::ostringstream os;
    for (int i = 0; i < size_array; ++i)
    {
        os << (add_quot_mark ? "\"" : "") << array[i] << (add_quot_mark ? "\"" : "");
        if (i != size_array - 1)
        {
            os << ", ";
        }

        if (i != size_array - 1 && (i + 1) % num_in_one_line == 0)
        {
            os << "\n";
        }
    }
    return os.str();
}


template <typename Key, typename Value, typename Cmp>
std::string
Utilities::get_rearranged_map(
    const std::map<Key, Value, Cmp>& map)
{
    std::ostringstream os;
    for (typename std::map<Key, Value, Cmp>::const_iterator pa = map.begin();
            pa != map.end(); ++pa)
    {
        os << "(" << pa->first << ", " << pa->second << ")" << std::endl;
    }
    return os.str();
}


template <typename T>
std::string
Utilities::get_rearranged_enum_vector (
    const std::vector<T>& array,
    const bool add_quot_mark,
    const int num_in_one_line)
{
    const int size_array = static_cast<int>(array.size());
    std::ostringstream os;
    for (int i = 0; i < size_array; ++i)
    {
        os << (add_quot_mark ? "\"" : "") << enum_to_string<T>(array[i]) << (add_quot_mark ? "\"" : "");
        if (i != size_array - 1)
        {
            os << ", ";
        }

        if (i != size_array - 1 && (i + 1) % num_in_one_line == 0)
        {
            os << "\n";
        }
    }
    return os.str();
}


template<typename T>
inline void
Utilities::fill_vector (
    const int dim,
    const T* arr,
    T* origin_array)
{
    FREEMAPS_ASSERT (dim == 2 || dim == 3);
    for (int idim = 0; idim < dim; ++idim)
    {
        origin_array[idim] = arr[idim];
    }

    if (dim == 2)
    {
        origin_array[2] = 0.;
    }
}


template<typename T, typename Cmp>
inline void
Utilities::insert (
    const std::set<T, Cmp>& src,
    std::set<T, Cmp>& des)
{
    for (typename std::set<T, Cmp>::const_iterator ps = src.begin();
            ps != src.end(); ++ps)
    {
        des.insert (*ps);
    }
}


template<typename T, typename Cmp>
inline void
Utilities::insert (
    const std::vector<T>& src,
    std::set<T, Cmp>& des)
{
    for (std::size_t i = 0; i < src.size(); ++i)
    {
        des.insert (src[i]);
    }
}


template<typename T, typename Cmp>
inline std::set<T, Cmp>
Utilities::get_combined_set(
    const std::set<T, Cmp>& s1,
    const std::set<T, Cmp>& s2)
{
    std::set<T, Cmp> new_s (s1);
    insert (s2, new_s);
    return new_s;
}


template<typename Key, typename Value, typename Cmp>
inline std::map<Key, Value, Cmp>
Utilities::get_combined_map(
    const std::map<Key, Value, Cmp>& m1,
    const std::map<Key, Value, Cmp>& m2)
{
    std::map<Key, Value, Cmp> result (m1);
    for (typename std::map<Key, Value, Cmp>::const_iterator pos = m2.begin();
            pos != m2.end(); ++pos)
    {
        result.insert (*pos);
    }

    return result;
}


inline void
Utilities::compute_vol_and_areas_of_cell (
    const int dim,
    const double* dx,
    double& vol,
    double* areas)
{
    FREEMAPS_ASSERT (dim == 2 || dim == 3);
    vol = 1.;
    for (int i = 0; i < dim; ++i)
    {
        vol *= dx[i];

        if (dim == 2)
        {
            areas[i] = dx[(i + 1) % dim];
        }
        else if (dim == 3)
        {
            areas[i] = dx[(i + 1) % dim] * dx[(i + 2) % dim];
        }
    }
}


inline double
Utilities::get_cell_vol (
    const int dim,
    const double* dx)
{
    double vol = 1.;
    FREEMAPS_ASSERT (dim == 2 || dim == 3);
    for (int i = 0; i < dim; ++i)
    {
        vol *= dx[i];
    }

    return vol;
}


inline void
Utilities::compute_areas_of_cell (
    const int dim,
    const double* dx,
    double* areas)
{
    FREEMAPS_ASSERT (dim == 2 || dim == 3);
    for (int i = 0; i < dim; ++i)
    {
        if (dim == 2)
        {
            areas[i] = dx[(i + 1) % dim];
        }
        else if (dim == 3)
        {
            areas[i] = dx[(i + 1) % dim] * dx[(i + 2) % dim];
        }
    }
}


inline double
Utilities::get_area_of_cell(
    const int dim,
    const int direction,
    const double* dx)
{
    FREEMAPS_ASSERT (dim == 2 || dim == 3);

    double area = 1;
    for (int i = 0; i < dim - 1; ++i)
    {
        area *= dx[(direction + i + 1) % dim];
    }

    return area;
}


inline int
Utilities::get_processor_id (
    const int number,
    const std::vector<int>& proc_nums)
{
    int total = 0;
    int max_proc_id = 0;
    for (std::size_t i = 0; i < proc_nums.size(); ++i)
    {
        total += proc_nums[i];
        if (number < total)
        {
            return static_cast<int>(i);
        }
        max_proc_id = static_cast<int>(i);
    }

    return max_proc_id;
}


inline int
Utilities::trans_position_to_int (
    const char position)
{
    if (position == 'L')
    {
        return -1;
    }
    else if (position == 'C')
    {
        return 0;
    }
    else if (position == 'U')
    {
        return 1;
    }
    else
    {
        FREEMAPS_ERROR("error position");
        return 2;
    }
}


inline double
Utilities::degree2rad (
    const double angle_in_degree)
{
    return (angle_in_degree / 180. * FREEMAPS_PI);
}


inline double
Utilities::rad2degree(
    const double angle_in_rad)
{
    return (angle_in_rad / FREEMAPS_PI * 180.);
}


template<typename T>
inline bool
Utilities::is_invalid_value (
    const T value)
{
    return (Utilities::enum_to_string<T>(value) == "INVALID_VALUE");
}


template<typename T>
inline std::set<T>
Utilities::trans_vec_to_set (
    const std::vector<T>& array)
{
    std::set<T> set;
    for (std::size_t i = 0; i < array.size(); ++i)
    {
        if (set.count (array[i]) > 0)
        {
            FREEMAPS_ERROR ("when trans vector to set, there have same elems");
        }

        set.insert (array[i]);
    }

    return set;
}


template<typename T, typename Cmp>
inline std::vector<T>
Utilities::trans_set_to_vec (
    const std::set<T, Cmp>& s)
{
    std::vector<T> array (s.size());
    int i = 0;
    for (typename std::set<T, Cmp>::const_iterator ps = s.begin(); ps != s.end(); ++ps, ++i)
    {
        array[i] = *ps;
    }

    return array;
}


template<typename T>
inline void
Utilities::scale (
    std::vector<T>& vec,
    const T factor)
{
    for (std::size_t pos = 0; pos != vec.size(); ++pos)
    {
        vec[pos] *= factor;
    }
}


template<typename Key_type, typename Value_type, typename Cmp>
inline std::set<Key_type, Cmp>
Utilities::get_map_keys (
    const std::map<Key_type, Value_type, Cmp>& map)
{
    std::set<Key_type, Cmp> set;
    for (typename std::map<Key_type, Value_type, Cmp>::const_iterator pm = map.begin();
            pm != map.end(); ++pm)
    {
        set.insert (pm->first);
    }

    return set;
}


template<typename T>
std::set<std::string>
Utilities::get_acceptable_enum_values()
{
    /*
     * the enum should be ordered from 0 to the last item with name INVALID_VALUE.
     * and the enum item should not too much.
     */
    const int max_item_num = 10000;
    std::set<std::string> values;
    for (int i = 0; i < max_item_num; i++)
    {
        std::string val = enum_to_string<T> ((T) i);
        if (val == "INVALID_VALUE")
        {
            break;
        }

        values.insert (val);

        if (i == max_item_num - 1)
        {
            FREEMAPS_ERROR ("in enum type of " << typeid (T).name() << ", no item of 'INVALID_VALUE' is set" << std::endl);
        }
    }
    return values;
}


template <typename T>
void
Utilities::add_reduced_tensor (
    std::vector<T>& tensor,
    const int start_index,
    const std::vector<T>& reduced_tensor)
{
    const int dim_axisymmetry = reduced_tensor.size();
    FREEMAPS_ASSERT (dim_axisymmetry == 3 || dim_axisymmetry == 4 || dim_axisymmetry == 6);
    const int dim = (dim_axisymmetry == 6 ? 3 : 2);

    if (dim == 2 )
    {
        if (dim_axisymmetry == 3)
        {
            /*
             * temsor is [xx xy xz; yx yy yz; zx zy zz], reduced_tensor is [xx,yy,xy]
             */
            tensor [start_index + 0] += reduced_tensor [0];
            tensor [start_index + 1] += reduced_tensor [2];
            tensor [start_index + 3] += reduced_tensor [2];
            tensor [start_index + 4] += reduced_tensor [1];
        }
        else if (dim_axisymmetry == 4)
        {
            /*
             * temsor is [xx xy xz; yx yy yz; zx zy zz],vector is [xx,yy,xy,zz]
             */
            tensor [start_index + 0] += reduced_tensor [0];
            tensor [start_index + 1] += reduced_tensor [2];
            tensor [start_index + 3] += reduced_tensor [2];
            tensor [start_index + 4] += reduced_tensor [1];
            tensor [start_index + 8] += reduced_tensor [3];
        }
    }
    else if (dim == 3)
    {
        /*
         * temsor is [xx xy xz; yx yy yz; zx zy zz],vector is [xx,yy,zz,xy,yz,xz]
         */
        tensor [start_index + 0] += reduced_tensor [0];
        tensor [start_index + 1] += reduced_tensor [3];
        tensor [start_index + 2] += reduced_tensor [5];
        tensor [start_index + 3] += reduced_tensor [3];
        tensor [start_index + 4] += reduced_tensor [1];
        tensor [start_index + 5] += reduced_tensor [4];
        tensor [start_index + 6] += reduced_tensor [5];
        tensor [start_index + 7] += reduced_tensor [4];
        tensor [start_index + 8] += reduced_tensor [2];
    }
    else
    {
        FREEMAPS_FIND_BUG();
    }
}


template<typename T>
int
Utilities::find_in_vector(
    const std::vector<T>& src,
    const T dst)
{
    int count = 0;
    for (std::size_t i = 0; i < src.size(); ++i)
    {
        if (dst == src[i])
        {
            ++count;
        }
    }

    return count;
}

} //namespace FreeMAPs
